package com.botarena.server.services;

import java.util.ConcurrentModificationException;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Transaction;

import com.botarena.server.dao.helper.NonTransactional;
import com.botarena.server.dao.helper.NonTransactionalReturn;
import com.botarena.server.dao.helper.PMF;
import com.botarena.server.dao.helper.Transactional;
import com.botarena.server.dao.helper.TransactionalReturn;

/**
 * Static DAO service. Wraps every access to Datastore.
 */
public class DAOService {
	
	private static final Logger log = Logger.getLogger(DAOService.class
			.getName());
	
	private static final int MAX_TRANSACTION_RETRY = 5;
	
	private static PersistenceManagerFactory pmf = PMF.get();
	
	// IMPORTANT
	// You can't create/delete different Entities in a datastore transaction.. :(
	public static <T> T execute(TransactionalReturn<T> transactionalReturn) {
		PersistenceManager pm = pmf.getPersistenceManager();
		Transaction tx = null;
		T ret = null;
		try {
			int retry = MAX_TRANSACTION_RETRY;
			while (retry != -1) {
				tx = pm.currentTransaction();
				tx.begin();
				ret = transactionalReturn.doInTransaction(pm);
				try {
					tx.commit();
					retry = -1;
				} catch (ConcurrentModificationException e) {
					if (retry == 0) {
						throw e;
					}
					--retry;
				}
			}
			
		} catch (Exception e) {
			tx.rollback();
			
		} finally {
			if (tx.isActive()) {
				tx.rollback();
			}
			pm.close();
		}
		return ret;
	}
	
	// IMPORTANT
	// You can't create/delete different Entities in a datastore transaction.. :(
	public static void execute(Transactional transactional) {
		PersistenceManager pm = pmf.getPersistenceManager();
		Transaction tx = null;
		try {
			int retry = MAX_TRANSACTION_RETRY;
			log.info("TRANSACTION remaining retries="+retry);
			while (retry != -1) {
				log.info("TRANSACTION trying to begin transaction");
				tx = pm.currentTransaction();
				tx.begin();
				log.info("TRANSACTION calling Transactional");
				transactional.doInTransaction(pm);
				try {
					log.info("TRANSACTION trying to commit transaction");
					tx.commit();
					retry = -1;
				} catch (ConcurrentModificationException e) {
					log.info("TRANSACTION concurrent modification exception!!!");
					if (retry == 0) {
						log.info("TRANSACTION cannot retry!!! throwing concurrent modification exception");
						throw e;
					}
					--retry;
				}
			}
			
		} catch (Exception e) {
			log.info("TRANSACTION outer-catched exception ("+e+"). rollbacking.");
			tx.rollback();
			
		} finally {
			log.info("TRANSACTION finalizing.");
			if (tx.isActive()) {
				log.info("TRANSACTION was active. rollbacking.");
				tx.rollback();
			}
			pm.close();
		}
		log.info("TRANSACTION ended.");
	}
	
	public static void execute(NonTransactional nonTransactional) {
		PersistenceManager pm = pmf.getPersistenceManager();
		try {
			nonTransactional.doOutsideTransaction(pm);
		} finally {
			pm.close();
		}
	}
	
	public static <T> T execute(NonTransactionalReturn<T> nonTransactionalReturn) {
		PersistenceManager pm = pmf.getPersistenceManager();
		T ret;
		try {
			ret = nonTransactionalReturn.doOutsideTransaction(pm);
		} finally {
			pm.close();
		}
		return ret;
	}
}
